﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
using FDSimpleModelGenerator.Json;
using FDSimpleModelGenerator.Logging;

namespace FDSimpleModelGenerator.Aop
{
    /// <summary>
    /// 记录执行日志
    /// </summary>
    public class SystemLogAttribute : AbstractInterceptorAttribute
    {
        private ISystemLog<SystemLogAttribute> _log;
        /// <summary>
        /// 操作前日志
        /// </summary>
        public bool LogActionBeforeCall { get; set; }

        /// <summary>
        /// 操作后日志
        /// </summary>
        public bool LogActionAfterCall { get; set; }

        private AopOptions _aopOptions;

        public void Initialize()
        {
            //ISystemLog<SystemLogAttribute> log, AopOptions aopOptions
            _log = AopContext.Current.GetLogger();
            _aopOptions = AopContext.Current.GetAopOptions();
        }

        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            // 由于是Attribute，所以并不会保持成员对象
            Initialize();

            if (!CheckIfIntercept(context))
            {
                return;
            }
            try
            {
                if (LogActionBeforeCall || _aopOptions.LogActionBeforeCall)
                {
                    _log.Info($"calling the method {context.Implementation}::{context.ProxyMethod}...");
                }
                await next(context);
            }
            catch (Exception e)
            {
                _log.Error($"system execute exception {e.Message}, {e.Source}");
                throw;
            }
            finally
            {
                if (LogActionAfterCall || _aopOptions.LogActionAfterCall)
                {
                    if (IsLogResult(context))
                    {
                        var returnType = context.ProxyMethod.ReturnType;
                        object resultValue = context.ReturnValue;
                        if (typeof(Task) == returnType.BaseType)
                        {
                            Task.WaitAll((Task)context.ReturnValue);

                            var pResult = returnType.GetProperty("Result", BindingFlags.Instance | BindingFlags.Public);
                            var value = pResult.GetValue(context.ReturnValue);
                            var vType = value.GetType();
                            resultValue = value;
                            if (vType.IsClass && vType != typeof(string) || vType.IsInterface)
                            {
                                resultValue = value.ToJson();
                            }
                        }
                        else if ((returnType.IsClass || returnType.IsInterface) && returnType != typeof(string))
                        {
                            resultValue = context.ReturnValue.ToJson();
                        }
                        else { }
                        _log.Info($"result for {context.Implementation}::{context.ProxyMethod}: {resultValue}");
                    }
                }
            }
        }

        private bool IsLogResult(AspectContext context)
        {
            if (context.ProxyMethod.ReturnType == typeof(void))
            {
                return false;
            }
            var ignore = context.ProxyMethod.Name.IndexOf('.') == -1 && _aopOptions.IgnoreResultForInterface; // 暂时以方法名不带二级区分作为接口与实现
            return !ignore;
        }

        private bool CheckIfIntercept(AspectContext context)
        {
            if (_aopOptions.AopLogType == AopLogType.MethodType)
            {
                var method = context.ProxyMethod.Name;
                var needMethods = _aopOptions.LogForActionType?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                      .ToList() ?? new List<string>();
                if (needMethods.Count == 0)
                {
                    var ignoreMethods = _aopOptions.NonLogPredicates
                                            ?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                            .ToList() ?? new List<string>();
                    if (ignoreMethods.Count == 0) // 不排除任何方法，则所有方法拦截
                    {
                        return true;
                    }

                    foreach (var ignoreMethod in ignoreMethods)
                    {
                        if (IsContains(ignoreMethod, method))
                        {
                            return true;
                        }
                    }
                }
                else
                {
                    foreach (var needMethod in needMethods)
                    {
                        if (IsContains(needMethod, method))
                        {
                            return true;
                        }
                    }
                }

                return false; // 其他不能拦截
            }

            return true;
        }

        /// <summary>
        /// 是否包含在规则里
        /// </summary>
        /// <param name="source">匹配规则串</param>
        /// <param name="str">方法名</param>
        /// <returns></returns>
        private bool IsContains(string source, string str)
        {
            source = source.Trim();
            var isBlurMatch = source.IndexOf('*') != -1;
            if (isBlurMatch)
            {
                if (source.StartsWith("*"))
                {
                    return str.EndsWith(source.Substring(1)); // 后匹配 *xxx
                }

                return str.StartsWith(source.Substring(0, source.Length - 1)); // 前匹配 xxx*
            }

            return string.Compare(source, str, StringComparison.OrdinalIgnoreCase) == 0;
        }
    }
}
